namespace MultiLineComments
{

    using System;
    using System.Runtime.InteropServices;
    using System.Text.RegularExpressions;
    using System.Diagnostics;
    using System.Globalization;
    using System.ComponentModel.Design;
    using Microsoft.Win32;
    using Microsoft.VisualStudio.OLE.Interop;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.TextManager.Interop;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.Text.Tagging;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Classification;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Formatting;
    using Microsoft.VisualStudio.Utilities;
    using Microsoft.VisualStudio.Editor;
    using System.Collections.Generic;
    using Microsoft.VisualStudio.Text.Operations;

    using System.Text;
    using System.Drawing;
    using EnvDTE;
    using EnvDTE80;


    public class CppWrapper : BaseWrapper
    {
        public CppWrapper(IVsTextView textViewAdapter, IVsEditorAdaptersFactoryService EditorFactory) :
            base(textViewAdapter, EditorFactory) { }

        // variables related to the cstring wrapping case
        int cstring_cursor_position;

        // helper function for the new quoted string wrapping logic
        void add_string_line(string add)
        {

            if (cstring_cursor_position > wrapped.Length)
            {
                if (wrapped.Length > 0)
                {
                    cstring_cursor_position += prefix.Length;
                }
            }

            wrapped += prefix + add;

            string posfix = @"""" + endline;

            if (cstring_cursor_position > wrapped.Length)
            {
                cstring_cursor_position += posfix.Length;
            }

            wrapped += posfix;

        }




        // this version of the wrapping is used when we're dealing with
        // a multiline quoted string.  it handles spaces and endlines a
        // bit differently than the comment wrapping code.
        public void cstring_wraptext(string text, int num_chars)
        {
            string next_add = "";
            string old_text = text;

            Match m = Regex.Match(text, @"^\s+");
            if (m.Success)
            {
                string match = m.Value;
                next_add += match;
                text = text.Remove(0, match.Length);
            }

            while (text.Length > 0)
            {


                m = Regex.Match(text, @"^\S+");
                if (m.Success)
                {
                    string match = m.Value;
                    text = text.Remove(0, match.Length);

                    bool overflow = (next_add.Length == 0 && match.Length > num_chars);

                    if (overflow && wrapped.Length == 0)
                    {
                        too_long_case = true;
                        return;
                    }

                    // if the word doesn't overflow the allowed characters, add it to the
                    // current line.
                    if (match.Length + next_add.Length <= num_chars || overflow)
                    {
                        next_add += match;
                    }
                    // if the word doesn't overlap, then make it the start of a new line.
                    else
                    {

                        add_string_line(next_add);

                        next_add = match;
                    }

                    // add any trailing space
                    m = Regex.Match(text, @"^\s+");
                    if (m.Success)
                    {
                        string spaces = m.Value;

                        next_add += spaces;

                        text = text.Remove(0, spaces.Length);
                    }

                    // now that the trailing spaces have made the line longer, we may 
                    // need to start up a new line. 
                    if (next_add.Length >= num_chars)
                    {
                        add_string_line(next_add);
                        next_add = "";
                    }

                }
                else
                {
                    // this case should never happen.
                    MessageBox.Show("hit a logic error in comment wrapping code.  you should be able to disable the multiline comment extension under Tools/Extension Manager.");
                    break;
                }
            }
            if (next_add.Length > 0) add_string_line(next_add);
        }

        //
        // this is a hook that we call from the main event handler, it
        // will apply the multiline quoted string wrapping if needed.
        //
        protected override bool wrapline(TextLineChange c)
        {

            if (wbuffer.EditInProgress) return false;

            if (r0 != r1) return false;

            cstring_cursor_position = absolute_cursor - linestart;

            if (cstring_cursor_position < 1) return false;

            lines.GetLengthOfLine(r1, out ln);
            lines.GetLineText(r1, 0, r1, ln, out line);
            //string firstline = line;

            m = Regex.Match(line, @"^(\s*"")(.*)""\s*$");
            if (!m.Success) return false;

            char cp = line[cstring_cursor_position - 1];
            if (cp == '"') return false;

            prefix = m.Groups[1].Value;
            string firstline = line;
            string longline = m.Groups[2].Value;
            int r;
            for (r = r1 + 1; r < nlines; r++)
            {
                if (longline.EndsWith("\\n") || longline.EndsWith("\\r"))
                {
                    break;
                }

                lines.GetLengthOfLine(r, out ln);
                lines.GetLineText(r, 0, r, ln, out line);

                m = Regex.Match(line, @"^(\s*)""(.*)""\s*$");
                if (!m.Success) break;
                longline = longline + m.Groups[2].Value;

            }



            wrapped = "";
            endline = "\r\n";

            cstring_wraptext(longline, 60);

            if (too_long_case) return false;

            if (wrapped.StartsWith(firstline)) return false;

            ITextEdit edit = wbuffer.CreateEdit();
            if (edit == null) return false;

            lines.GetLengthOfLine(r - 1, out ln);

            int rstart, rend, vs;
            view.GetNearestPosition(r0, 0, out rstart, out vs);
            view.GetNearestPosition(r, 0, out rend, out vs);

            edit.Replace(rstart, rend - rstart, wrapped);

            edit.Apply();


            int newposition = linestart + cstring_cursor_position;
            wview.VisualElement.Dispatcher.BeginInvoke(new Action(() =>
            {
                int pr, pc;

                if (view.GetLineAndColumn(newposition, out pr, out pc) == VSConstants.S_OK)
                {
                    view.SetCaretPos(pr, pc);
                }

            })
            );

            return true;
        }

    }


}